using System;
using System.Reflection;

namespace Com.ContentRendering.Api.DataEngine.Handler.Child
{
    /// <summary/>
    internal class CachedIndexerChildHandler : AbstractChildHandler
    {
        /// <summary/>
        internal CachedIndexerChildHandler(
            ReflectionCache reflectionCache,
            object data,
            string name)
            : base(
            reflectionCache,
            data,
            name) {}

        /// <summary/>
        internal override object Invoke()
        {
            Type type = base._data.GetType();

            // has it NOT been cached
            if (!base._reflectionCache.IsIndexerValid(type))
            {
                return base._successor.Invoke();
            }

            // fetch the cached method
            MethodInfo methodInfo = base._reflectionCache.FetchIndexerKeyMethod(type);
            
            // was a contains key method found
            if (methodInfo != null)
            {
                // execute the method and catch the result
                bool found = (bool)methodInfo.Invoke(
                    base._data,
                    new object[] { base._name });

                // return nothing if the key was not found
                if (!found)
                {
                    return null;
                }
            }

            // fetch it
            PropertyInfo propertyInfo = base._reflectionCache.FetchIndexer(type, base._name);

            // execute the property
            return propertyInfo.GetValue(
                this._data, 
                new object[] { base._name });
        }

        /// <summary/>
        private static string FetchIndexerName(Type type)
        {
            // fetch all of the custom attributes
            object[] attributes = type.GetCustomAttributes(true);

            // search the attributes for the default member
            foreach (Attribute attribute in attributes)
            {
                // make sure the type is correct
                if (!typeof(DefaultMemberAttribute).IsInstanceOfType(attribute))
                {
                    continue;
                }

                DefaultMemberAttribute defaultMemberAttribute = (DefaultMemberAttribute)attribute;

                // read the name of the attribute
                return defaultMemberAttribute.MemberName;
            }

            return null;
        }

        /// <summary/>
        private static MethodInfo FetchContainsKeyMethod(
            Type type)
        {
            MethodInfo[] methodInfos = type.GetMethods();

            // check each method on the class
            foreach (MethodInfo methodInfo in methodInfos)
            {
                // make sure the name matches
                if (methodInfo.Name != Constant.METHOD_CONTAINS_KEY)
                {
                    continue;
                }

                ParameterInfo returnParameterInfo = methodInfo.ReturnParameter;
                ParameterInfo[] parameterInfos = methodInfo.GetParameters();

                // if the return parameter is not a boolean then move on
                if (returnParameterInfo.ParameterType != typeof(bool))
                {
                    continue;
                }

                // make sure there is one and only one input parameter
                if (parameterInfos.Length != 1)
                {
                    continue;
                }

                // make sure the input parameter is a string
                if (parameterInfos[0].ParameterType != typeof(string))
                {
                    continue;
                }
                
                return methodInfo;
            }

            return null;
        }
    }
}
